/*
 * * Java Basics Practical Tasks module1
 * 4. Calendar Seeker task
 */

package module1;

import java.util.*;

/**
 *
 * @author Antonina Cherednichenko
 */
public class CalendarSeeker {
    private static final int LEAP_MAIN_DIVIDER = 4;
    private static final int LEAP_FIRST_ADDITIONAL_DIVIDER = 100;
    private static final int LEAP_SECOND_ADDITIONAL_DIVIDER = 400;
    private static final int MONTH_QUANTITY = 12;
    private static final int DAY_QUANTITY = 365;
    private static final int WEEK_SIZE = 7;
    private static final int DAY_START = 1;
    private static final int DAY_STOP = 31;
    private static final int MONTH_START = 1;
    private static final int MONTH_STOP = 12;
    private static final int YEAR_START = 1900;
    private static final int YEAR_STOP = 2100;

    /**
     * Mapping value from range [0..6] to the day of week
     */
    private static String getDayOfWeek(int value) {
        String[] weekDays = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
        return weekDays[value];
    }

    /**
     * Getting value from range [0..6], special algorithm is used
     * Unfortunatelly, I don't know what do 4800, 153, 32045 constants mean (for me they are also magic)
     */
    private static int getValueOfWeek(CalendarDate date) {
        int a = (14 - date.getMonth())/MONTH_QUANTITY;
        int yearCount = date.getYear() + 4800 - a;
        int monthCount = date.getMonth() + MONTH_QUANTITY * a - 3;
        int dayNumber = date.getDay() + (153 * monthCount + 2)/5 + DAY_QUANTITY * yearCount;
        dayNumber = dayNumber + yearCount / LEAP_MAIN_DIVIDER - yearCount / LEAP_FIRST_ADDITIONAL_DIVIDER +
                    yearCount/LEAP_SECOND_ADDITIONAL_DIVIDER - 32045;
        return dayNumber % WEEK_SIZE;
    }

    /**
     * Getting valid input for day, month and year (day from 1..31, month from 1..12, year from 1900..2100
     * Otherwise exception is thrown and is handled inside main function
     */
    private static CalendarDate getValidInput(Scanner in) throws InputMismatchException {
        System.out.println("Calendar Seeker:");
        int day = getValidDayMonthYear(in, "day", DAY_START, DAY_STOP);
        int month = getValidDayMonthYear(in, "month", MONTH_START, MONTH_STOP);
        int year = getValidDayMonthYear(in, "year", YEAR_START, YEAR_STOP);
        return new CalendarDate(day, month, year);
    }

    /**
     * Getting valid day or month or year
     * Function created to avoid copy-paste while getting this values
     */
    private static int getValidDayMonthYear(Scanner in, String whatInput, int lowerLimit,
                                 int upperLimit) throws InputMismatchException {
        System.out.printf("Enter %s, from %d..%d\n", whatInput, lowerLimit, upperLimit);
        int inputVal = in.nextInt(); // can throw exception
        if ((inputVal < lowerLimit) || (inputVal > upperLimit)){
            throw new InputMismatchException();
        }
        return inputVal;
    }

    /**
     * Function for linking all logic together, is called from main Module1 class
     */
    public static void testFunction() {
        Scanner in = new Scanner (System.in);
        System.out.println("Testing function for Calendar Seeker");
        try {
            CalendarDate date = getValidInput(in);
            String res = getDayOfWeek(getValueOfWeek(date));
            System.out.printf("For input %s result of Calendar Seeker is %s\n", date, res);
        } catch (InputMismatchException exc) {
            System.out.println("Wrong input, retry with correct conditions");
        } finally {
            //in.close();
        }
    }
 }

/*
 * Inner representation of user date
 */
 class CalendarDate {
     private int day;
     private int month;
     private int year;
     CalendarDate(int day, int month, int year) {
        this.day = day;
        this.month = month;
        this.year = year;
     }

     int getDay() {
         return day;
     }

     int getMonth() {
         return month;
     }

     int getYear() {
         return year;
     }

    @Override
     public String toString() {
         return String.format("Date: %d.%d.%d", day, month, year);
     }
 }
